#ifndef KCPTRANSPORT_HPP_
#define KCPTRANSPORT_HPP_

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <malloc.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <stdarg.h>
#include <fcntl.h>


#include <vector>
#include <iostream>
#include <utility>
#include <thread>
#include <chrono>
#include <functional>
#include <atomic>

#include <sstream>

#include "Trace.hpp"
//#include "header.h"                                                                                                                            
#include "ThreadPool.hpp"
#include "UdpSocket.hpp"
#include "Kcp.hpp"

//=================KcpSession==========================

class KcpSession : public Kcp<true>
{
public:

      using ptr  = std::shared_ptr<KcpSession>;

      KcpSession(const KcpOpt &opt,const sockaddr_in &addr,const UdpSocket::ptr &socket);

      const sockaddr_in& GetAddr() const { return addr_;}

      std::string GetAddrToString() const;

      void SetAddr(const sockaddr_in &addr) {addr_ = addr;}
//private:
public:
      sockaddr_in          addr_;
      UdpSocket::ptr       socket_;
};

KcpSession::KcpSession(const KcpOpt &opt,const sockaddr_in &addr,const UdpSocket::ptr &socket) : Kcp<true>(opt),
                                                                                                      addr_(addr),
                                                                                                      socket_(socket)
{
    TRACE("");
     SetSendFunc([this](const void *buf,std::size_t size,void *)
     {
            if(socket_->SendTo(buf,size,addr_) == -1)
              perror("sendto error");
     });
}

std::string 
KcpSession::GetAddrToString() const
{
    TRACE("");
    std::stringstream ss;
    ss << inet_ntoa(addr_.sin_addr) << ":" << ntohs(addr_.sin_port);
    return ss.str();
}


//=================KcpTransport==========================
                                                                                                                                               
class KcpTransport
{
public:
    //set Session
    using ptr  = std::shared_ptr<KcpTransport>;

    //session's remote end
    KcpTransport();//启动udpate、recv线程。
    ~KcpTransport();

    void setSession(KcpSession::ptr& session) { session_ = session; }

    //as client
    bool connect(const std::string& host, int port);

    //as server
    typedef void(*AcceptCallback)(const KcpTransport*);//Accept Callback
    bool listen(int port, int backlog, const AcceptCallback& accept_cb);
    KcpTransport::ptr accept();

    //core func
    int32_t read(uint8_t* buffer, uint32_t size, bool immediately=false);
    int32_t write(uint8_t* buffer, uint32_t size, bool immediately=false);


    //thread start
    void start();

    //get
    const std::string& getConnectedHost(){return _connected_host;}
    int getConnectedPort(){return _connected_port;}

    int getServerPort(){return server_port_;}
   //int getLocalPort(){return _local_port;}                                                                                                  

    //set
    void setNoDelay(bool nodelay);
    void setKeepAlive(bool use, uint32_t idle, uint32_t interval, uint32_t count);

    //other
    //void parseHeader(const Header& header); //TODO must impletetion
    void close();


    //KcpSession
private:
    void    Update();
    bool    Check(int ret);
    void    HandleSession(const KcpSession::ptr &session, int length);
    uint64_t GetConv(const void *data);
    KcpSession::ptr NewSession(uint64_t conv, const sockaddr_in &addr);

    static constexpr uint32_t body_size = 1024*4;
protected:
    //virtual void HandleClose(const KcpSession::ptr& session) = 0;
    //virtual void HandleMessage(const KcpSession::ptr& session,const std::string &msg) = 0;
    //virtual void HandleConnection(const KcpSession::ptr& session) = 0;
    //KcpSession end
                                                                                                                                               
//private:
public:
    bool initializeSocket();
    bool initializeSocketWithPort(int local_port, int remote_port);
    bool setNonBlocking();
    bool setSocket(int sock);

    void handleKcpUpdateThread();
    void handleKcpRecvThread();

private:
    std::unique_ptr<std::thread>  _thread_update;

    //opt
    bool _is_server;
    bool _async_connected;
    bool _closed;

    bool _expecting_read;
    bool _expecting_write;

    //addr
    sockaddr_storage _server_address;
    sockaddr_storage _local_address;
    socklen_t sa_len;
    socklen_t la_len;

    //port
    AcceptCallback _accept_cb;

    std::string cached_remote_host;

    int _flags;//用于判断是否是SYNCHRONOUS

    //connected addr
    std::string _connected_host;
    int _connected_port;


private:
    //using SessionMap = std::unordered_map<uint64_t, KcpSession::ptr>;
    //SessionMap      sessions_;
    KcpSession::ptr session_;
    //KcpServer
    UdpSocket::ptr  socket_;
    ThreadPool      pool_;
    std::vector<char> buf_;
    //KcpServer End
    //KcpClient
    //UdpSocket::ptr socket_;
    sockaddr_in server_addr_;//when as server, it's client addr;Otherwise, it's server's addr;
    int server_port_;
    //KcpClient End
};



#define ROS_INVALID_SOCKET -1 
#define SYNCHRONOUS -1 
KcpTransport::KcpTransport()//启动udpate、recv线程。
{
    TRACE("");

}

KcpTransport::~KcpTransport()
{
    TRACE("");
    _thread_update->join();
}

void  
KcpTransport::start()
{
    //启动handleKcpUpdate处理线程池
    pool_.Start(1 /*number of threads*/ );
    //启动update and recv主循环检测线程
    _thread_update = std::make_unique<std::thread>(&KcpTransport::handleKcpUpdateThread, this);
}

//as client
bool 
KcpTransport::connect(const std::string& host, int port)
{
    TRACE("");

    _connected_host = host;
    _connected_port = port;

    //客户端启动的时候，设置一个conv
    //uint64_t conv = GetConv(buf_.data()); 
    uint64_t conv = 111;//暂定为111 


    server_addr_.sin_family         =   AF_INET;
    server_addr_.sin_port           =   htons(port);
    server_addr_.sin_addr.s_addr    =   inet_addr(host.c_str());

    socket_ = std::make_shared<UdpSocket>("0.0.0.0", 7777, AF_INET);
    //socket_->SetNoblock();

    session_ = NewSession(conv, server_addr_)//自动将socket_传递进去sesson_的socket指针上


    TRACE("");


    //判断_sockfd是否分配成功
    if( socket_->fd_ == ROS_INVALID_SOCKET ) { return false; }

    //setNonBlocking();
    
    //向对面请求连接，并阻塞等待。
    if(session_->Update(iclock64()))
    { 
        //HandleClose();
        return false;
    }

    TRACE("");

    std::string buf(1024*4, 'a');
    sockaddr_in addr;
    int len = socket_->RecvFrom(const_cast<char*>(buf.data()), buf.length(), addr);

    TRACE("");

    if(addr.sin_port != server_addr_.sin_port ||
       addr.sin_addr.s_addr != server_addr_.sin_addr.s_addr)
        return true;//
    if(!Check(len))return true;
    buf.resize(len);

    len = session_->Input(const_cast<char*>(buf.data()), len);

    TRACE("");

    if(len!=0)
    {
        TRACE("Input error = ", len);
        return false;
    }


    //TODO 将host转换成ipv4的地址，然后调用connect连接对端 
    
    if(!initializeSocket())return false;  //该阶段启动心跳检测 并会设置noBlocking

    start();

    return true;
}


//NOTE XXX TODO DEBUG BUG !!! Houlc
bool 
KcpTransport::listen(int port, int backlog, const AcceptCallback& accept_cb)
{   
    TRACE("");

    _is_server = true;
    _accept_cb = accept_cb;



    //TODO 设置服务器地址和端口号和地址大小
    socket_ = std::make_shared<UdpSocket>(/*ip*/ "0.0.0.0", port, AF_INET);

    //TODO 将服务器地址和端口号绑定在该地址上
    if(!socket_->Bind()) { exit(-1);}
    
    //if(!initializeSocket())return false; //在其中设置心跳检测，配置pollset等
    
    return true;
    
}

KcpTransport::ptr
KcpTransport::accept()
{
    TRACE("");

    if(!_is_server) return nullptr; //必须得是服务器模式，才能使用此函数. 而只有listen之后，才变成服务器模式

    //用该新的套接字，创建一个新的transportPtr返回。
    sockaddr_in addr;

    //该处应该设置为阻塞 TODO
    int len = socket_->RecvFrom(buf_.data(),body_size,addr);
    //收到之后，再设置为非阻塞。

    if(!Check(len))
      return std::make_shared<KcpTransport>();

    uint64_t conv = GetConv(buf_.data()); 

    KcpOpt opt;
    opt.conv                = conv;
    opt.interval            = 10;
    opt.nodelay             = true;
    opt.trigger_fast_resend = 5;
    opt.offline_standard    = 1000;

    KcpSession::ptr session = std::make_shared<KcpSession>(opt,addr,socket_);

    KcpTransport::ptr kcpTransport = std::make_shared<KcpTransport>(); 
    kcpTransport->setSession(session);

    kcpTransport->initializeSocket();

    kcpTransport->start();
    //kcpTransport->HandleSession(session,len);
    return kcpTransport;
    //返回这个session
}

//core func
//设置immediately之后，这个函数就可以单独进行测试了。
int32_t 
KcpTransport::read(uint8_t* buffer, uint32_t size, bool immediately)
{
    TRACE("");

    if(immediately)
    {
        if(!socket_){

        }

        Update();

        sockaddr_in addr;

        int len = socket_->RecvFrom(buf_.data(),body_size,addr);

        if(!Check(len))
          continue;

        uint64_t conv = GetConv(buf_.data());


         if(session_->opt_.conv != conv){
             //如果管理的多个session的话，这里应该新建一个session，用这个conv号和对端通信
             //但目前只建立一个session，那么就只用这个session和对端通信就好。
             session_->opt.conv                = conv;
             session_->opt.interval            = 10;
             session_->opt.nodelay             = true;
             session_->opt.trigger_fast_resend = 5;
             session_->opt.offline_standard    = 1000;
             session_->addr_                   = addr;
             session_->socket_                 = socket_;
         }
        
        
        int ret = session->Input(buf_.data(),length);

        if(ret != 0)
        {
           TRACE("Input error = ",ret);
           return;
        }

    }

    //TODO 先检查kcp内核中是否可读，不可读则返回错误
    
    //可读则kcp_recv读出数据
    int len = 0;
    do{
        ret = session_->Recv(buffer, size);
        if(ret == -1){
            TRACE("buffer size too small");
            exit(-1);
        }
        if(ret == 0)
            break;
        len += ret;
    }while(1);

    return len;
}
int32_t 
KcpTransport::write(uint8_t* buffer, uint32_t size, bool immediately)
{
    TRACE("");

    int ret =  session_->Send(buffer, size);

    if(immediately) {
        session_->Flush();
    }

    return ret;
}

//get

//set
void 
KcpTransport::setNoDelay(bool nodelay)
{
    TRACE("");

    session_->opt_.nodelay = true;
}

//TODO
void 
KcpTransport::setKeepAlive(bool use, uint32_t idle, uint32_t interval, uint32_t count)
{
    TRACE("");
    //先默认使用 
    //set session_'s opt_
    //session_->opt_.heartbeat = interval; 
}

/*
void  
KcpTransport::parseHeader(const Header& header) //TODO must impletetion
{
    TRACE("");

    std::string nodelay="1";
    if(header.getValue("tcp_nodelay", nodelay) && nodelay == "1")
    {
        setNoDelay(true);
    }
}
*/
void 
KcpTransport::close()
{
    TRACE("");
    /*
    Callback discoonect_cb; //断连回调函数
    if(!_closed)
    {
        {
            boost::recursive_mutex::scoped_lock lock(_close_mutex);
            if(!_closed)
            {
                _closed = true;
                if(_sockfd == ROS_INVALID_SOCKET)return ;//表明已经关掉了

                //if(poll_set){poll_set->delSocket};//delete socket from pollset
                
                //XXX  将各个回调函数重置
                //_disconnect_cb , _read_cb, _write_cb, _accept_cb;
                
            }
        }
        
    }

    if(discoonect_cb)
    {
        discoonect_cb(shared_from_this());//断连的时候，将参数传递过去
    }
    */
}

bool
KcpTransport::initializeSocketWithPort(int local_port, int remote_port)
{
    //客户端启动的时候，设置一个conv
    //uint64_t conv = GetConv(buf_.data()); 
    uint64_t conv = 111;//暂定为111 


    KcpOpt opt;
    opt.conv                = conv;
    opt.interval            = 10;
    opt.nodelay             = true;
    opt.trigger_fast_resend = 5;
    opt.offline_standard    = 1000;

    socket_ = std::make_shared<UdpSocket>("0.0.0.0", local_port, AF_INET);
    socket_->SetNoblock();

    std::string host = "0.0.0.0";

    server_addr_.sin_family     =   AF_INET;
    server_addr_.sin_port     =   htons(remote_port);
    server_addr_.sin_addr.s_addr     =   inet_addr(host.c_str());



    session_ = std::make_shared<KcpSession>(opt,server_addr_,socket_);

    TRACE("");


    //判断_sockfd是否分配成功
    if( socket_->fd_ == ROS_INVALID_SOCKET ) { return false; }
    return true;
}

//private
bool 
KcpTransport::initializeSocket()
{
    TRACE("");

    if(socket_->fd_==ROS_INVALID_SOCKET)return false;//使用该函数前，应该已经创建过socket了

    if(!setNonBlocking())return false;//设置nonBlocking

    setKeepAlive(true, 60, 10, 9);//设置心跳检测

    //TODO 设置cached_remote_host
    
    //TODO 如果本地端口还没有设置，那就配置一下本地端口
    
    setNoDelay(true);//如果用tcp，则设置该选项
    
    //配置pollset，将本socket添加到pollset上
    
    return true;
}

bool 
KcpTransport::setNonBlocking()
{
    TRACE("");

    if(!(_flags & SYNCHRONOUS ))
    {
        int result = socket_->SetNoblock();
        if( result != 0){ close(); return false;}// set error
    }
    return true;
}
bool 
KcpTransport::setSocket(int sock)
{
    TRACE("");

    socket_->fd_ = sock;
    return initializeSocket();
}

void 
KcpTransport::handleKcpUpdateThread()
{
    TRACE("");

    //按照设置好的频率，不停的updateKcp，发送ack等
     do
     {
         usleep(10);
    TRACE("");

         Update();
    TRACE("");

         sockaddr_in addr;

         int len = socket_->RecvFrom(buf_.data(),body_size,addr);
    TRACE("");

         if(!Check(len))
           continue;

         uint64_t conv = GetConv(buf_.data());
         if(session_->opt_.conv != conv){
             //如果管理的多个session的话，这里应该新建一个session，用这个conv号和对端通信
             //但目前只建立一个session，那么就只用这个session和对端通信就好。
             session_->opt.conv                = conv;
             session_->opt.interval            = 10;
             session_->opt.nodelay             = true;
             session_->opt.trigger_fast_resend = 5;
             session_->opt.offline_standard    = 1000;
             session_->addr_                   = addr;
             session_->socket_                 = socket_;
         }

    TRACE("");
         HandleSession(session_,len);//如果收到消息，将其传递给用户的处理数据的回调函数，放到另一个线程执行
    TRACE("");

     }while(1);

}


void
KcpTransport::Update()
{
    TRACE("");

     if(!session_->Update(iclock64()))
     {
         //continue;
         //ok
     }
     else
     {
         //wrong
        // HandleClose(session_);//关闭的时候的回调函数
     }
}

bool
KcpTransport::Check(int ret)
{
    TRACE("");

    if(ret == -1)
    {
        if(errno!=EAGAIN)
            perror("recvfrom error!!");
        return false;
    }
    if(ret<KcpAttr::KCP_HEADER_SIZE)
        return false;
    return true;
}


KcpSession::ptr 
KcpTransport::NewSession(uint64_t conv,const sockaddr_in &addr)
{
    TRACE("");

     KcpOpt opt;
     opt.conv                = conv;
     opt.interval            = 10;
     opt.nodelay             = true;
     opt.trigger_fast_resend = 5;
     opt.offline_standard    = 1000;

     KcpSession::ptr session = std::make_shared<KcpSession>(opt,addr,socket_);

     //HandleConnection(session);
     //建立连接后，此处调用用户定义的回调函数

     return session;
}

uint64_t 
KcpTransport::GetConv(const void* buf)
{
     return *(uint64_t*)(buf);
}

void 
KcpTransport::HandleSession(const KcpSession::ptr &session,int length)
{
    TRACE("");

      int ret = session->Input(buf_.data(),length);

      if(ret != 0)
      {
         TRACE("Input error = ",ret);
         return;
      }

      do
      {
         int len = session->Recv(buf_.data(),body_size);

         if(len == -1)
         {
            TRACE("body size too small");
            exit(-1);
         }

         if(len == 0)
           break;

         std::string msg(buf_.data(),buf_.data() + len);

         TRACE(msg);

         pool_.Put([this,session,msg]()
         {
            //HandleMessage(session,msg);//用户层回调函数
         });
      }while(1);
}


//KcpServerEnd................................

#endif
